package main

import (
	"fmt"
	"io/ioutil"
	"strconv"
	"time"
	"container/list"
)

type greatest struct {
	gg int
	a int
	b int
	c int
	d int
	where string
	pos int
}


func main() {
	start := time.Now()
	array := readNumber("./grid.txt")
	newArray := createArray(array)
	grid := createGrid(newArray)
	gg := splitGrid(grid)
	fmt.Printf("-------------->Greatest is: (%v*%v*%v*%v)=%v at %v %v\n", gg.a, gg.b, gg.c, gg.d, gg.gg, gg.where, gg.pos)
	stop := time.Now()
	fmt.Println("Execution time:", stop.Sub(start))
}

func findGreatestProduct(l *list.List, where string, pos int) *greatest {
	i := 1
	gg := 0
	aa := 0
	bb := 0
	cc := 0
	dd := 0
	if l.Len() < 4 {
		//return 1, 0, 0, 0, 0, "", 0
		return nil
	} else {
		for e := l.Front(); e != nil; e = e.Next() {
			if e.Next().Next().Next() == nil {
				break;
			} else {
				a := e.Value.(int)
				b := e.Next().Value.(int)
				c := e.Next().Next().Value.(int)
				d := e.Next().Next().Next().Value.(int)
				fmt.Printf("#%v a=%v, b=%v, c=%v, d=%v\n", i, a, b, c, d)
				product := a * b * c *d
				if product > gg {
					gg = product
					aa = a
					bb = b
					cc = c
					dd = d
				}
				i++
			}
		}
		fmt.Printf("+++Greatest product: (%v*%v*%v*%v)=%v at %v %v\n", aa, bb, cc, dd, gg, where, pos)

	}
	return &greatest{gg, aa, bb, cc, dd, where, pos}
	//return 1
}

func splitGrid(grid [][]int) *greatest {
	//var gg, a, b, c, d, pos int = 0,0,0,0,0,0
	//var where string
	rlen := len(grid)
	clist := list.New()
	rlist := list.New()
	dlist := list.New()
	ddlist := list.New()
	glist := list.New()
	//rows && cols
	for i := 0; i < rlen; i++ {
		for k := 0; k < rlen; k++ {
			//columns
			fmt.Printf("grid[%v][%v]=%v\n", i, k, grid[i][k])
			clist.PushBack(grid[i][k])
			//rows
			fmt.Printf("grid[%v][%v]=%v\n", k, i, grid[k][i])
			rlist.PushBack(grid[k][i])

		}
		fmt.Println("----------columns------------------")
		glist.PushBack(findGreatestProduct(clist, "column", i))
		clist.Init()
		fmt.Println("----------rows---------------------")
		glist.PushBack(findGreatestProduct(rlist, "row", i))
		rlist.Init()
		fmt.Println("-----------------------------------")
	}
	//diags a
	g := rlen-1
	for n := 3; n < rlen; n++ {
		j := 0
		for k := n; k >= 0; k-- {
			fmt.Printf("%vth diag-> grid[%v][%v]=%v\n", n, k, j, grid[k][j])
			dlist.PushBack(grid[k][j])
			fmt.Printf("%vth inverted diag-> grid[%v][%v]=%v\n", n, j, g-n+j, grid[j][g-n+j])
			ddlist.PushBack(grid[j][g-n+j])
			j++
		}
		glist.PushBack(findGreatestProduct(dlist, "diag", n))
		dlist.Init()
		glist.PushBack(findGreatestProduct(ddlist, "inverted diag", n))
		ddlist.Init()
	}
	//diags b
	//g := rlen-1
	for n := 3; n < rlen-1; n++ {
		j := 0
		for k := n; k >= 0; k-- {
			fmt.Printf("%vth diag b-> grid[%v][%v]=%v\n", n, g-j, k, grid[g-j][g-n+j])
			dlist.PushBack(grid[g-j][g-n+j])
			fmt.Printf("%vth inverted diag b-> grid[%v][%v]=%v\n", n, g-n+j, j, grid[g-n+j][j])
			ddlist.PushBack(grid[g-n+j][j])
			j++
		}
		glist.PushBack(findGreatestProduct(dlist, "diag b", n))
		dlist.Init()
		glist.PushBack(findGreatestProduct(ddlist, "inverted diag b", n))
		ddlist.Init()
	}
	gg := 0
	var ggg *greatest
	for e := glist.Front(); e != nil; e = e.Next() {
		if e.Value.(*greatest).gg > gg {
			ggg = e.Value.(*greatest)
			gg = e.Value.(*greatest).gg
		}
	}
	return ggg
}

func createGrid(array []int) [][]int {
	array2 := make([][]int, 20)
	for i := 0; i < 20; i++ {
		array2[i] = make([]int, 20)
	}
	a := 0
	b := 0
	for i := 0; i < len(array); i++ {
		if a == 20 {
			a = 0
			b++
			i--
		} else {
			array2[a][b] = array[i]
			a++
		}
	}
	fmt.Println(array2)
	return array2
}

func createArray(array []int) []int {
	array2 := make([]int, 400)
	k := 0
	for i := 0; i < len(array); i += 2 {
		a := array[i]
		b := array[i+1]
		a *= 10
		n := a + b
		array2[k] = n
		k++
	}
	return array2
}

//Reads the file and returns only the actual numbers in a slice of ints
func readNumber(filename string) []int {
	n := 0
	bigNumber := make([]byte, 800)
	big := make([]int, 800)
	//Automatically reads the whole file and returns an array of bytes
	buf, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	//We get rid of any byte that is not a number (like '\n')
	//so we keep only the actual numbers. Those are the bytes 48 to 57
	//48 is number 0 and 57 is number 9.
	for k := 0; k < len(buf); k++ {
		if int(buf[k]) >= 48 && int(buf[k]) <= 57 {
			bigNumber[n] = buf[k]
			n++
		}
	}
	//We convert the bytes to ints so we can make calculations
	for i := 0; i < len(bigNumber); i++ {
		big[i], _ = strconv.Atoi(string(bigNumber[i]))
	}
	return big
}



